package com.xinsoft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinsoft.common.CountBean;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.constant.Const;
import com.xinsoft.entity.dto.*;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.ScmSaleListVo;
import com.xinsoft.entity.vo.ScmSalePriceVo;
import com.xinsoft.entity.vo.ScmSaleVo;
import com.xinsoft.mapper.*;
import com.xinsoft.service.*;
import com.xinsoft.utils.TokenUtil;
import lombok.Synchronized;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZYM
 * @description 针对表【scm_sale(销售订单)】的数据库操作Service实现
 * @createDate 2023-07-04 14:59:06
 */
@Service
public class ScmSaleServiceImpl extends ServiceImpl<ScmSaleMapper, ScmSale> implements ScmSaleService {

    @Resource
    private ScmSaleMapper saleMapper;
    @Resource
    private ScmSaleItemMapper saleItemMapper;
    @Resource
    private ScmSalePriceMapper salePriceMapper;
    @Resource
    private FmsAccountsReceivableMapper accountsReceivableMapper;
    @Resource
    private ScmSaleNoticeMapper saleNoticeMapper;
    @Resource
    private ScmSaleReturnMapper saleReturnMapper;
    @Resource
    private ScmSaleOutstockMapper saleOutstockMapper;
    @Resource
    private ScmPurchasePlanMapper purchasePlanMapper;


    @Autowired
    private SysDocumentNumService documentNumService;
    @Autowired
    private ScmDocumentService documentService;
    @Autowired
    private ScmSaleItemService saleItemService;
    @Autowired
    private FmsAccountsReceivableService accountsReceivableService;
    @Autowired
    private SysUserService userService;
    @Autowired
    private SysClientService clientService;
    @Autowired
    private SysLevelPriceRefService levelPriceRefService;


    @Override
    public IPage<ScmSaleListVo> findPageListByParam(ScmSalePageParam param) {
        if (param.getTabType() != null && param.getTabType() == 3) {
            Integer deptId = Objects.requireNonNull(TokenUtil.getUser()).getDeptId();
            List<SysUser> userList = userService.list(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getDeptId, deptId));
            List<Integer> userIdList = userList.stream().map(SysUser::getId).collect(Collectors.toList());
            param.setMyTeam(userIdList);
        }
        Page<ScmSaleListVo> page = new Page<>(param.getCurrentPage(), param.getPageSize());
        IPage<ScmSaleListVo> pageList = saleMapper.findPageListByParam(page, param);

        // 被引用时计算可用数量
        if (param.getCiteType() != null && pageList.getRecords().size() > 0) {
            List<ScmSaleListVo> voList = this.calculateAvailableQuantity(pageList.getRecords(), param.getCiteType());
            List<ScmSaleListVo> collect = voList.stream().filter(obj -> obj.getPrimaryUnitCount().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
            pageList.setRecords(CollectionUtil.isNotEmpty(collect) ? collect : null);
            pageList.setTotal(CollectionUtil.isNotEmpty(collect) ? collect.size() : 0);
        }
        return pageList;
    }

    /**
     * 计算可用数量
     */
    public List<ScmSaleListVo> calculateAvailableQuantity(List<ScmSaleListVo> voList,Integer citeType){
        if (voList.size() > 0) {
            List<Integer> sourceIds = voList.stream().map(ScmSaleListVo::getId).distinct().collect(Collectors.toList());
            switch (citeType) {
                case 0:
                    // 发货通知引用销售订单计算可用数量
                    List<CountBean> countBeanList = saleNoticeMapper.getUsedCountOfSaleNotice(sourceIds, Const.ApproveType.XSDD);
                    Map<String, CountBean> noticeUsedCountMap = countBeanList.stream().collect(Collectors.toMap(CountBean::getId, o -> o));
                    List<CountBean> usedCountOfSaleReturn = saleReturnMapper.getUsedCountOfSaleReturn(sourceIds, Const.ApproveType.FHTZ);
                    Map<String, CountBean> returnUsedCountMap = usedCountOfSaleReturn.stream().collect(Collectors.toMap(CountBean::getId, countBean -> countBean));
                    voList.forEach(vo -> {
                        CountBean countBean = noticeUsedCountMap.get(String.join("_", vo.getId().toString(), vo.getMaterialId().toString()));
                        CountBean returnCountBean = returnUsedCountMap.get(String.join("_", vo.getId().toString(), vo.getMaterialId().toString()));
                        if (countBean != null && returnCountBean != null) {
                            vo.setPrimaryUnitCount(vo.getSaleNum().subtract(countBean.getPrimaryUnitUsedCount().subtract(returnCountBean.getPrimaryUnitUsedCount())));
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum().subtract(countBean.getAuxiliaryUnitUsedCount().subtract(returnCountBean.getAuxiliaryUnitUsedCount())));
                        }
                        if (countBean != null && returnCountBean == null) {
                            vo.setPrimaryUnitCount(vo.getSaleNum().subtract(countBean.getPrimaryUnitUsedCount()));
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum().subtract(countBean.getAuxiliaryUnitUsedCount()));
                        }
                        if (countBean == null) {
                            vo.setPrimaryUnitCount(vo.getSaleNum());
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum());
                        }

                    });
                    break;
                case 1:
                    // 销售出库引用销售订单计算可用数量
                    // 销售出库已用的销售订单数量
                    List<CountBean> countBeanList1 = saleOutstockMapper.getUsedCountOfSaleOutstock(sourceIds, Const.ApproveType.XSDD);
                    Map<String, CountBean> usedCountMap1 = countBeanList1.stream().collect(Collectors.toMap(CountBean::getId, o -> o));
                    voList.forEach(vo -> {
                        CountBean countBean = usedCountMap1.get(String.join("_", vo.getId().toString(), vo.getMaterialId().toString()));
                        if (countBean != null) {
                            vo.setPrimaryUnitCount(vo.getSaleNum().subtract(countBean.getPrimaryUnitUsedCount()));
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum().subtract(countBean.getAuxiliaryUnitUsedCount()));
                        } else {
                            vo.setPrimaryUnitCount(vo.getSaleNum());
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum());
                        }
                    });
                    break;
                case 2:
                    // 采购计划引用销售订单计算可用数量
                    // 采购计划已用的销售订单数量
                    List<CountBean> countBeanList2 = purchasePlanMapper.getUsedCountOfPurchasePlan(sourceIds, Const.ApproveType.XSDD);
                    Map<String, CountBean> usedCountMap2 = countBeanList2.stream().collect(Collectors.toMap(CountBean::getId, o -> o));
                    voList.forEach(vo -> {
                        CountBean countBean = usedCountMap2.get(String.join("_", vo.getId().toString(), vo.getMaterialId().toString()));
                        if (countBean != null) {
                            vo.setPrimaryUnitCount(vo.getSaleNum().subtract(countBean.getPrimaryUnitUsedCount()));
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum().subtract(countBean.getAuxiliaryUnitUsedCount()));
                        } else {
                            vo.setPrimaryUnitCount(vo.getSaleNum());
                            vo.setAuxiliaryUnitCount(vo.getSupportSaleNum());
                        }
                    });
                    break;
                default:
                    break;
            }
        }
        return voList;
    }

    @Override
    public List<ScmSaleListVo> findListByParam(ScmSalePageParam param) {
        // IPage的参数 size<0 ，临时不分页
        Page<ScmSaleListVo> page = new Page<>(param.getCurrentPage(), -1);
        IPage<ScmSaleListVo> pageListByParam = saleMapper.findPageListByParam(page, param);
        return pageListByParam.getRecords();
    }

    @Override
    @Synchronized
    public Integer tempSave(ScmSaleDTO scmSaleDTO) {
        if (CollectionUtils.isEmpty(scmSaleDTO.getScmSaleItemList())) {
            throw new BDException("物料列表不能为空");
        }
        if (CollectionUtils.isEmpty(scmSaleDTO.getFmsAccountsReceivableList())) {
            throw new BDException("应收账款不能为空！");
        }
        scmSaleDTO.setAuditSign(Const.AUDIT_STATUS_WAIT);
        scmSaleDTO.setDoClose(Const.CloseStatus.NOT_CLOSED);
        scmSaleDTO.setApproveStatus(Const.ApproveStatus.STAGING);
        scmSaleDTO.setApplyPersonId(TokenUtil.getUserId());
        scmSaleDTO.setApplyTime(new Date());

        List<ScmSaleItem> scmSaleItemList = scmSaleDTO.getScmSaleItemList();
        List<FmsAccountsReceivable> fmsAccountsReceivableList = scmSaleDTO.getFmsAccountsReceivableList();
        if (Objects.isNull(scmSaleDTO.getId())) {
            int count = this.count(Wrappers.lambdaQuery(ScmSale.class).eq(ScmSale::getContractCode, scmSaleDTO.getContractCode()));
            if (count > 0) {
                throw new BDException("合同编号已存在，请重新生成");
            }
            // 更新流水号
            documentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.SALE_DOCUMENT);
            // 保存销售订单主表
            this.save(scmSaleDTO);
        } else {
            // 删除物料列表
            List<ScmSaleItem> scmSaleItems = saleItemService.list(Wrappers.<ScmSaleItem>lambdaQuery().eq(ScmSaleItem::getSaleId, scmSaleDTO.getId()));
            List<Integer> itemIds = scmSaleItems.stream().map(ScmSaleItem::getId).collect(Collectors.toList());
            saleItemService.removeByIds(itemIds);

            // 删除附件与销售订单的关联关系
            documentService.remove(Wrappers.<ScmDocument>lambdaQuery().eq(ScmDocument::getScmId, scmSaleDTO.getId()));
            // 删除应收账款
            accountsReceivableService.remove(Wrappers.<FmsAccountsReceivable>lambdaQuery().eq(FmsAccountsReceivable::getSaleId, scmSaleDTO.getId()));
            // 更新销售订单主表
            this.updateById(scmSaleDTO);
        }
        // 保存物料列表
        scmSaleItemList.forEach(scmSaleItem -> {
            scmSaleItem.setSaleId(scmSaleDTO.getId());
            scmSaleItem.setDoSendCheck(Objects.isNull(scmSaleItem.getDoSendCheck()) ? 0 : scmSaleItem.getDoSendCheck());
            scmSaleItem.setDoClose(Const.CloseStatus.NOT_CLOSED);
        });
        saleItemService.saveBatch(scmSaleItemList);
        // 保存应收账款
        fmsAccountsReceivableList.forEach(fmsAccountsReceivable -> fmsAccountsReceivable.setSaleId(scmSaleDTO.getId()));
        accountsReceivableService.saveBatch(fmsAccountsReceivableList);
        // 保存附件关系
        List<SysFileHis> fileHisList = scmSaleDTO.getSysFileHisList();
        documentService.saveDocument(fileHisList, scmSaleDTO.getId(), Const.ApproveType.XSDD);
        return scmSaleDTO.getId();
    }

    @Override
    public void del(List<Integer> ids) {
        List<ScmSale> saleList = this.list(Wrappers.<ScmSale>lambdaQuery()
                .eq(ScmSale::getApproveStatus, Const.ApproveStatus.STAGING)
                .in(ScmSale::getId, ids));
        if (saleList.size() < ids.size()) {
            throw new BDException("操作失败，只能删除暂存的销售订单");
        }
        documentService.remove(Wrappers.lambdaQuery(ScmDocument.class).in(ScmDocument::getScmId, ids).eq(ScmDocument::getScmType,Const.ApproveType.XSDD));
        accountsReceivableService.remove(Wrappers.lambdaQuery(FmsAccountsReceivable.class).in(FmsAccountsReceivable::getSaleId, ids));
        saleItemService.remove(Wrappers.lambdaQuery(ScmSaleItem.class).in(ScmSaleItem::getSaleId, ids));
        this.removeByIds(ids);
    }

    @Override
    public ScmSaleVo detail(Integer id) {
        ScmSaleVo saleVo = saleMapper.getSaleById(id);
        // 物料列表
        saleVo.setScmSaleItemList(saleItemMapper.listBySaleId(id));
        // 应收账款
        List<FmsAccountsReceivable> accountsReceivableList = accountsReceivableMapper.selectList(Wrappers.<FmsAccountsReceivable>lambdaQuery().eq(FmsAccountsReceivable::getSaleId, id));
        saleVo.setFmsAccountsReceivableList(accountsReceivableList);
        // 附件列表
        saleVo.setSysFileHisList(documentService.getFileHisListByScmId(id, Const.ApproveType.XSDD));
        return saleVo;
    }

    @Override
    public void close(List<Integer> ids) {
        List<ScmSale> scmSales = this.listByIds(ids);
        scmSales.forEach(scmSale -> {
            if (Objects.equals(scmSale.getDoClose(), Const.CloseStatus.CLOSED)) {
                throw new BDException("销售订单已关闭，无需操作");
            }
            // if (Objects.equals(scmSale.getAuditSign(), Const.AUDIT_STATUS_WAIT)) {
            //     throw new BDException("不能关闭未审核的销售订单");
            // }
            if (!Objects.equals(scmSale.getApproveStatus(), Const.ApproveStatus.APPROVED)) {
                throw new BDException("不能关闭未审批通过的销售订单");
            }
            scmSale.setDoClose(Const.CloseStatus.CLOSED);
        });
        this.updateBatchById(scmSales);
    }

    @Override
    public void reverseClose(List<Integer> ids) {
        List<ScmSale> scmSales = this.listByIds(ids);
        scmSales.forEach(scmSale -> {
            if (Objects.equals(scmSale.getApproveStatus(), Const.CloseStatus.NOT_CLOSED)) {
                throw new BDException("销售订单未关闭，无需操作");
            }
            scmSale.setDoClose(Const.CloseStatus.NOT_CLOSED);
        });
        this.updateBatchById(scmSales);
    }

    @Override
    public void lineClose(ScmSaleItemDTO scmSaleItemDTO) {
        List<ScmSaleItem> items = new ArrayList<>();
        scmSaleItemDTO.getIds().forEach(id -> {
            ScmSaleItem scmSaleItem = new ScmSaleItem();
            scmSaleItem.setId(id);
            scmSaleItem.setCloseTime(new Date());
            scmSaleItem.setDoClose(Const.CloseStatus.CLOSED);
            scmSaleItem.setCloseReason(scmSaleItemDTO.getCloseReason());
            items.add(scmSaleItem);
        });
        saleItemService.updateBatchById(items);
    }

    @Override
    public ScmSalePriceVo historyPrice(Integer clientId, Integer materialId) {
        ScmSalePriceVo vo = new ScmSalePriceVo();
        SysClient client = clientService.getById(clientId);
        SysLevelPriceRef priceRef = levelPriceRefService.getOne(Wrappers.lambdaQuery(SysLevelPriceRef.class)
                .eq(SysLevelPriceRef::getLevelId, client.getLevelId())
                .eq(SysLevelPriceRef::getMaterialId, materialId));
        ScmSalePriceVo priceVo = salePriceMapper.historyPrice(clientId, materialId);
        if (Objects.nonNull(priceVo)) {
            vo = priceVo;
        }
        if (Objects.nonNull(priceRef)) {
            vo.setBasePrice(priceRef.getSalePrice());
        }
        return vo;
    }

    @Override
    public List<ScmSaleListVo> findList(List<Integer> idList, List<Integer> materialIdList,Integer citeType) {
        ScmSalePageParam param = new ScmSalePageParam();
        param.setIdList(idList);
        param.setMaterialIdList(materialIdList);
        List<ScmSaleListVo> voList = saleMapper.findPageListByParam(param);
        return this.calculateAvailableQuantity(voList,citeType);
    }


}




